package com.liangwj.springbootquickstart.jdk8.streamApi;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <b>Description:</b> Stream API <br/>
 * @author Lingwenjun
 * @date 2019/1/9-17:24
 * @version 1.0
 */
public class CollectionStreamDemo {

    /**
     * 创建Stream
     */
    public static void demo() {
        //从List对象获取其对应的Stream对象
        List<String> nameList = Arrays.asList("Liang", "wen", "jun");
        nameList.forEach(System.out::println);

        //Stream<String> strStream = nameList.stream();

        //使用Stream静态方法of来创建Stream
        Stream<String> strOfStream = Stream.of("asd", "adsf");
        Stream<Integer> intOfStream = Stream.of(1, 2, 3);

        //使用Stream静态方法generate来创建Stream
        Stream<String> generateStream = Stream.generate(() -> "asd");

        //使用Stream静态方法iterate来创建Stream
        //这段代码就是先获取一个无限长度的正整数集合的Stream，然后取出前10个打印。
        Stream.iterate(0, i -> i + 1).limit(10).forEach(System.out::println);

        //针对Map的操作
        /*Map<String, Object> strMap = new HashMap<String, Object>(){
            {
                put("1", "asd");
                put("2", "sdfg");
            }
        };
        strMap.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v) );*/
    }

    /**
     * 流转换
     * 流转换是指从一个流中读取数据，并将转换后的数据写入到另外一个流中。流转换操作都是懒加载，
     * 多个转换操作只会在汇聚操作的时候融合起来，一次循环完成。
     */
    public static void streamConversion() {
        List<String> nameList = Arrays.asList("Liang", "wen", "jun", "jun", "liang");
        //distinct
        //对于Stream中包含的元素进行去重操作（去重逻辑依赖元素的equals方法），新生成的Stream中没有重复的元素。
        List<String> nameListNew= nameList.stream().distinct().collect(Collectors.toList());
        nameList.forEach(System.out::println);//使用迭代器Iterate来迭代循环,性能较差
        nameListNew.forEach(System.out::println);//使用流来循环,性能应该较高

        //filter
        //对于Stream中包含的元素使用给定的过滤函数进行过滤操作, 新生成的Stream只包含符合条件的元素
        Stream<String> stringStream = nameList.stream().filter(l -> l.length() <= 3);
        List<String> newList = stringStream.collect(Collectors.toList());
        System.out.println("newList = " + newList);

        //map
        //对于Stream中包含的元素使用给定的转换函数进行转换操作, 新生成的Stream只包含转换生成的元素
        Stream<String> stringStream1 = nameList.stream().map(String::toUpperCase);
        List<String> newList1 = stringStream1.collect(Collectors.toList());
        System.out.println("newList1 = " + newList1);

        //limit
        //对于Stream进行截断操作, 获取其前N个元素, 如果原Stream中包含的元素个数小于N, 那就获取其所有元素
        Stream<String> limit = nameList.stream().limit(2L);
        List<String> newList2 = limit.collect(Collectors.toList());
        System.out.println("newList2 = " + newList2);

        Stream<Integer> limit1 = Stream.iterate(0, i -> i + 1).limit(5);
        System.out.println("limit1 = " + limit1.collect(Collectors.toList()));

        //skip
        //返回一个丢弃原Stream的前N个元素后剩下的元素组成新的Stream, 如果原Stream中包含的元素个数小于N, 那么返回空的Stream
        Stream<String> skip = nameList.stream().skip(2L);
        List<String> newList3 = skip.collect(Collectors.toList());
        System.out.println("newList3 = " + newList3);

        //peek
        //生成一个包含原Stream的所有元素的新Stream, 同时会提供一个消费函数(Consumer实例), 新的Stream每个元素被消费的时候都会执行给定的消费函数
        //peek接收一个没有返回值的λ表达式，可以做一些输出，外部处理等。map接收一个有返回值的λ表达式，之后Stream的泛型类型将转换为map参数λ表达式返回的类型
        Stream<String> peek = nameList.stream().peek(System.out::println);//与MAP的区别: map是带return的,peek处理Consumer的函数,void类型
        List<String> newList4 = peek.collect(Collectors.toList());
        System.out.println("newList4 = " + newList4);

        //sorted
        //对一个Stream进行排序操作
        Stream<String> sorted = nameList.stream().sorted().sorted(Comparator.comparing(String::length).reversed());
        List<String> newList5 = sorted.collect(Collectors.toList());
        System.out.println("newList5 = " + newList5);

    }

    /**
     * 聚合操作
     */
    public static void streamConverge() {

        //reduce

        //我们用过reduce方法实现求和操作
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        Integer integer = list.stream().reduce((sum, i) -> sum + i).get();//求和
        Integer maxValue = list.stream().reduce((a, b) -> a > b ? a: b).get();//求和
        System.out.println("integer = " + integer);
        System.out.println("maxValue = " + maxValue);

        //当流为空时会返回标识值，这样我们就不再需要处理Optional类了。
        List<Integer> list1 = Collections.EMPTY_LIST;
        Integer integer1 = list1.stream().reduce(0, (sum, i) -> sum + i);
        System.out.println("integer1 = " + integer1);

        //注意区分与一个参数的Reduce方法的不同：它多了一个初始化的值，因此计算的顺序是identity与a[0]进行二合运算，结果与a[1]再进行二合运算，最终与a[n-1]进行二合运算。
        Stream<String> stringStream = Stream.of("a", "c", "b");
        String reduce = stringStream.reduce("[value]", (s1, s2) -> s1.concat(",").concat(s2));
        System.out.println("reduce = " + reduce);

        //我们还可以通过reduce实现累加器函数
        Stream<String> stringStream1 = Stream.of("1", "2", "3", "4", "5");
        Integer reduce1 = stringStream1.reduce(5, (sum, word) -> sum + word.length(), (sum1, sum2) -> sum1 + sum2);
        System.out.println("reduce1 = " + reduce1);

        //collect

        //直接使用上面Reduce模拟Filter的示例进行演示(使用lambda语法）：
        Stream<String> s1 = Stream.of("aa", "ab", "c", "ad");
        Predicate<String> predicate = s -> s.contains("a");
        System.out.println(s1.parallel().collect((Supplier<ArrayList<String>>) ArrayList::new,
                                                (array, s) -> {if (predicate.test(s)) array.add(s); },
                                                ArrayList::addAll));

        //collect可以把Stream中的要有元素收集到一个结果容器中（比如Collection）
        List<Integer> numList = Arrays.asList(1,2,3, null, 4,53, null,12, 67);
        List<Integer> noNullList = numList.stream()
                .filter(Objects::nonNull)//等价于filter(num -> num != null)
                //Collectors提供的工具类
                .collect(Collectors.toList());
                //等价于lambda:
                //.collect(ArrayList::new,
                //        ArrayList::add,
                //        ArrayList::addAll);

                //等价于非lambda:
                //.collect(() -> new ArrayList<Integer>(),
                //        (lists, item) -> lists.add(item),
                //        (list_1, list_2) -> list_1.addAll(list_2));
        System.out.println("noNullList = " + noNullList);

    }

    public static void main(String[] args) {
        //CollectionStreamDemo.demo();
        //CollectionStreamDemo.streamConversion();
        CollectionStreamDemo.streamConverge();
    }
}
